﻿package com.rbac.system.service.impl;

import com.rbac.system.common.Result;
import com.rbac.system.service.CaptchaService;
import com.rbac.system.service.SystemConfigService;
import com.rbac.system.util.RedisUtil;
import com.rbac.system.vo.CaptchaVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import java.util.UUID;

/**
 * 验证码服务实现类
 */
@Slf4j
@Service
public class CaptchaServiceImpl implements CaptchaService {
    
    @Autowired
    private SystemConfigService systemConfigService;
    
    @Autowired
    private RedisUtil redisUtil;
    
    private static final String CAPTCHA_PREFIX = "captcha:";
    private static final String SLIDER_PREFIX = "slider:";
    private static final String CHARS = "ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
    private static final Random random = new Random();
    
    @Override
    public Result<CaptchaVO> generateImageCaptcha() {
        try {
            if (!systemConfigService.getBooleanConfig("captcha.enabled", false)) {
                return Result.error("验证码功能未启用");
            }
            
            String captchaId = UUID.randomUUID().toString();
            int length = systemConfigService.getIntConfig("captcha.length", 4);
            int width = systemConfigService.getIntConfig("captcha.width", 120);
            int height = systemConfigService.getIntConfig("captcha.height", 40);
            int expireTime = systemConfigService.getIntConfig("captcha.expire", 300);
            boolean noiseEnabled = systemConfigService.getBooleanConfig("captcha.noise.enabled", true);
            
            // 生成验证码文本
            String text = generateRandomText(length);
            
            // 创建图片
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            
            // 设置抗锯齿
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            
            // 设置背景色
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, width, height);
            
            // 绘制干扰线
            if (noiseEnabled) {
                drawNoise(g, width, height);
            }
            
            // 绘制验证码文本
            drawText(g, text, width, height);
            
            g.dispose();
            
            // 转换为Base64
            String imageBase64 = imageToBase64(image);
            
            // 保存到Redis
            redisUtil.set(CAPTCHA_PREFIX + captchaId, text.toLowerCase(), expireTime);
            
            CaptchaVO captchaVO = new CaptchaVO();
            captchaVO.setCaptchaId(captchaId);
            captchaVO.setType("image");
            captchaVO.setImageBase64(imageBase64);
            captchaVO.setExpireTime(expireTime);
            captchaVO.setWidth(width);
            captchaVO.setHeight(height);
            
            log.info("生成图片验证码成功: captchaId={}", captchaId);
            return Result.success(captchaVO);
            
        } catch (Exception e) {
            log.error("生成图片验证码失败", e);
            return Result.error("生成验证码失败");
        }
    }
    
    @Override
    public Result<CaptchaVO> generateMathCaptcha() {
        try {
            if (!systemConfigService.getBooleanConfig("captcha.enabled", false)) {
                return Result.error("验证码功能未启用");
            }
            
            String captchaId = UUID.randomUUID().toString();
            int width = systemConfigService.getIntConfig("captcha.width", 120);
            int height = systemConfigService.getIntConfig("captcha.height", 40);
            int expireTime = systemConfigService.getIntConfig("captcha.expire", 300);
            
            // 生成数学运算
            int num1 = random.nextInt(10) + 1;
            int num2 = random.nextInt(10) + 1;
            String operator = random.nextBoolean() ? "+" : "-";
            int result;
            String expression;
            
            if ("+".equals(operator)) {
                result = num1 + num2;
                expression = num1 + " + " + num2 + " = ?";
            } else {
                if (num1 < num2) {
                    int temp = num1;
                    num1 = num2;
                    num2 = temp;
                }
                result = num1 - num2;
                expression = num1 + " - " + num2 + " = ?";
            }
            
            // 创建图片
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            
            // 设置抗锯齿
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            
            // 设置背景色
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, width, height);
            
            // 绘制干扰线
            drawNoise(g, width, height);
            
            // 绘制数学表达式
            g.setColor(Color.BLACK);
            g.setFont(new Font("Arial", Font.BOLD, 16));
            FontMetrics fm = g.getFontMetrics();
            int x = (width - fm.stringWidth(expression)) / 2;
            int y = (height + fm.getAscent()) / 2;
            g.drawString(expression, x, y);
            
            g.dispose();
            
            // 转换为Base64
            String imageBase64 = imageToBase64(image);
            
            // 保存到Redis
            redisUtil.set(CAPTCHA_PREFIX + captchaId, String.valueOf(result), expireTime);
            
            CaptchaVO captchaVO = new CaptchaVO();
            captchaVO.setCaptchaId(captchaId);
            captchaVO.setType("math");
            captchaVO.setImageBase64(imageBase64);
            captchaVO.setExpireTime(expireTime);
            captchaVO.setWidth(width);
            captchaVO.setHeight(height);
            
            log.info("生成数学运算验证码成功: captchaId={}, expression={}", captchaId, expression);
            return Result.success(captchaVO);
            
        } catch (Exception e) {
            log.error("生成数学运算验证码失败", e);
            return Result.error("生成验证码失败");
        }
    }
    
    @Override
    public Result<CaptchaVO> generateSliderCaptcha() {
        try {
            if (!systemConfigService.getBooleanConfig("captcha.enabled", false)) {
                return Result.error("验证码功能未启用");
            }

            String captchaId = UUID.randomUUID().toString();
            int width = 320;
            int height = 160;
            int sliderSize = 42;
            int expireTime = systemConfigService.getIntConfig("captcha.expire", 300);

            // 生成随机位置，确保滑块能够完全匹配
            int x = random.nextInt(width - sliderSize - 100) + 80; // 确保有足够的拖拽空间
            int y = random.nextInt(height - sliderSize - 20) + 10;

            // 创建简化的背景图片
            BufferedImage backgroundImage = createSimpleSliderBackground(width, height);

            // 创建简化的滑块图片，确保尺寸匹配
            BufferedImage sliderImage = createSimpleSliderPiece(backgroundImage, x, y, sliderSize);

            // 在背景图上创建精确的缺口
            cutSimpleSliderHole(backgroundImage, x, y, sliderSize);

            // 转换为Base64
            String backgroundBase64 = imageToBase64(backgroundImage);
            String sliderBase64 = imageToBase64(sliderImage);

            // 保存到Redis（保存正确的x坐标）
            redisUtil.set(SLIDER_PREFIX + captchaId, String.valueOf(x), expireTime);

            CaptchaVO captchaVO = new CaptchaVO();
            captchaVO.setCaptchaId(captchaId);
            captchaVO.setType("slider");
            captchaVO.setBackgroundImage(backgroundBase64);
            captchaVO.setSliderImage(sliderBase64);
            captchaVO.setExpireTime(expireTime);
            captchaVO.setWidth(width);
            captchaVO.setHeight(height);
            captchaVO.setY(y);

            log.info("生成滑块验证码成功: captchaId={}, x={}, y={}", captchaId, x, y);
            return Result.success(captchaVO);

        } catch (Exception e) {
            log.error("生成滑块验证码失败", e);
            return Result.error("生成验证码失败");
        }
    }
    
    @Override
    public Result<Void> verifyCaptcha(String captchaId, String userInput) {
        try {
            if (captchaId == null || userInput == null) {
                return Result.error("验证码ID和输入不能为空");
            }
            
            String correctAnswer = (String) redisUtil.get(CAPTCHA_PREFIX + captchaId);
            if (correctAnswer == null) {
                return Result.error("验证码已过期或不存在");
            }
            
            boolean caseSensitive = systemConfigService.getBooleanConfig("captcha.case.sensitive", false);
            String input = caseSensitive ? userInput : userInput.toLowerCase();
            
            if (!correctAnswer.equals(input)) {
                return Result.error("验证码错误");
            }
            
            // 验证成功后清除验证码
            clearCaptcha(captchaId);
            
            log.info("验证码验证成功: captchaId={}", captchaId);
            return Result.success();
            
        } catch (Exception e) {
            log.error("验证验证码失败: captchaId={}", captchaId, e);
            return Result.error("验证失败");
        }
    }
    
    @Override
    public Result<Void> verifySliderCaptcha(String captchaId, Integer x) {
        try {
            if (captchaId == null || x == null) {
                return Result.error("验证码ID和位置不能为空");
            }
            
            String correctXStr = (String) redisUtil.get(SLIDER_PREFIX + captchaId);
            if (correctXStr == null) {
                return Result.error("验证码已过期或不存在");
            }
            
            int correctX = Integer.parseInt(correctXStr);
            int tolerance = 10; // 允许10像素的误差，提升用户体验

            if (Math.abs(x - correctX) > tolerance) {
                return Result.error("验证失败，请重试");
            }
            
            // 验证成功后清除验证码
            redisUtil.del(SLIDER_PREFIX + captchaId);
            
            log.info("滑块验证码验证成功: captchaId={}, x={}, correctX={}", captchaId, x, correctX);
            return Result.success();
            
        } catch (Exception e) {
            log.error("验证滑块验证码失败: captchaId={}", captchaId, e);
            return Result.error("验证失败");
        }
    }
    
    @Override
    public void clearCaptcha(String captchaId) {
        redisUtil.del(CAPTCHA_PREFIX + captchaId);
        redisUtil.del(SLIDER_PREFIX + captchaId);
    }
    
    @Override
    public boolean isCaptchaRequired(String type) {
        if (!systemConfigService.getBooleanConfig("captcha.enabled", false)) {
            return false;
        }
        
        if ("login".equals(type)) {
            return systemConfigService.getBooleanConfig("captcha.login.enabled", false);
        } else if ("register".equals(type)) {
            return systemConfigService.getBooleanConfig("captcha.register.enabled", false);
        }
        
        return false;
    }
    
    @Override
    public String getCaptchaType() {
        return systemConfigService.getConfigValue("captcha.type", "image");
    }
    
    /**
     * 生成随机文本
     */
    private String generateRandomText(int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(CHARS.charAt(random.nextInt(CHARS.length())));
        }
        return sb.toString();
    }
    
    /**
     * 绘制干扰线
     */
    private void drawNoise(Graphics2D g, int width, int height) {
        g.setColor(Color.LIGHT_GRAY);
        for (int i = 0; i < 5; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g.drawLine(x1, y1, x2, y2);
        }
    }
    
    /**
     * 绘制文本
     */
    private void drawText(Graphics2D g, String text, int width, int height) {
        g.setFont(new Font("Arial", Font.BOLD, 20));
        FontMetrics fm = g.getFontMetrics();
        
        int x = (width - fm.stringWidth(text)) / 2;
        int y = (height + fm.getAscent()) / 2;
        
        // 绘制每个字符，添加随机颜色和位置偏移
        for (int i = 0; i < text.length(); i++) {
            g.setColor(new Color(random.nextInt(100), random.nextInt(100), random.nextInt(100)));
            char c = text.charAt(i);
            int charX = x + i * (fm.stringWidth(String.valueOf(c)) + 2);
            int charY = y + random.nextInt(6) - 3; // 随机上下偏移
            g.drawString(String.valueOf(c), charX, charY);
        }
    }
    
    /**
     * 创建简化版滑块背景图片
     */
    private BufferedImage createSimpleSliderBackground(int width, int height) {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();

        // 设置抗锯齿
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // 创建简单的渐变背景
        GradientPaint gradient = new GradientPaint(
            0, 0, new Color(240, 248, 255), // 浅蓝色
            width, height, new Color(176, 196, 222) // 浅钢蓝色
        );
        g.setPaint(gradient);
        g.fillRect(0, 0, width, height);

        // 添加简单的纹理点
        g.setColor(new Color(255, 255, 255, 80));
        for (int i = 0; i < 50; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            g.fillOval(x, y, 2, 2);
        }

        g.dispose();
        return image;
    }

    /**
     * 创建增强版滑块背景图片
     */
    private BufferedImage createEnhancedSliderBackground(int width, int height) {
        return createSimpleSliderBackground(width, height);
    }

    /**
     * 创建滑块背景图片（保持兼容性）
     */
    private BufferedImage createSliderBackground(int width, int height) {
        return createEnhancedSliderBackground(width, height);
    }
    
    /**
     * 创建简化版滑块拼图片段
     */
    private BufferedImage createSimpleSliderPiece(BufferedImage background, int x, int y, int size) {
        BufferedImage piece = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = piece.createGraphics();

        // 设置抗锯齿
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // 直接复制背景区域，使用简单的圆角矩形
        g.drawImage(background.getSubimage(x, y, size, size), 0, 0, null);

        // 绘制简单的边框
        g.setColor(new Color(255, 255, 255, 180));
        g.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g.drawRoundRect(1, 1, size - 2, size - 2, 8, 8);

        // 添加阴影效果
        g.setColor(new Color(0, 0, 0, 60));
        g.setStroke(new BasicStroke(1));
        g.drawRoundRect(2, 2, size - 4, size - 4, 6, 6);

        g.dispose();
        return piece;
    }

    /**
     * 创建增强版滑块拼图片段
     */
    private BufferedImage createEnhancedSliderPiece(BufferedImage background, int x, int y, int size) {
        return createSimpleSliderPiece(background, x, y, size);
    }

    /**
     * 创建滑块拼图片段（保持兼容性）
     */
    private BufferedImage createSliderPiece(BufferedImage background, int x, int y, int size) {
        return createEnhancedSliderPiece(background, x, y, size);
    }

    /**
     * 创建拼图形状路径
     */
    private GeneralPath createPuzzleShape(int x, int y, int size) {
        GeneralPath path = new GeneralPath();

        int puzzleSize = size;
        int bump = puzzleSize / 4; // 凸起大小

        // 开始绘制拼图形状
        path.moveTo(x, y);

        // 上边 - 带凸起
        path.lineTo(x + puzzleSize / 3, y);
        path.curveTo(x + puzzleSize / 3, y - bump, x + puzzleSize * 2 / 3, y - bump, x + puzzleSize * 2 / 3, y);
        path.lineTo(x + puzzleSize, y);

        // 右边 - 带凸起
        path.lineTo(x + puzzleSize, y + puzzleSize / 3);
        path.curveTo(x + puzzleSize + bump, y + puzzleSize / 3, x + puzzleSize + bump, y + puzzleSize * 2 / 3, x + puzzleSize, y + puzzleSize * 2 / 3);
        path.lineTo(x + puzzleSize, y + puzzleSize);

        // 下边 - 带凹陷
        path.lineTo(x + puzzleSize * 2 / 3, y + puzzleSize);
        path.curveTo(x + puzzleSize * 2 / 3, y + puzzleSize + bump, x + puzzleSize / 3, y + puzzleSize + bump, x + puzzleSize / 3, y + puzzleSize);
        path.lineTo(x, y + puzzleSize);

        // 左边 - 带凹陷
        path.lineTo(x, y + puzzleSize * 2 / 3);
        path.curveTo(x - bump, y + puzzleSize * 2 / 3, x - bump, y + puzzleSize / 3, x, y + puzzleSize / 3);
        path.closePath();

        return path;
    }
    
    /**
     * 在背景图上创建简化版缺口
     */
    private void cutSimpleSliderHole(BufferedImage background, int x, int y, int size) {
        Graphics2D g = background.createGraphics();

        // 设置抗锯齿
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // 清除圆角矩形区域
        g.setComposite(AlphaComposite.Clear);
        g.fillRoundRect(x, y, size, size, 8, 8);

        // 绘制缺口边框
        g.setComposite(AlphaComposite.SrcOver);
        g.setColor(new Color(0, 0, 0, 120));
        g.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g.drawRoundRect(x, y, size, size, 8, 8);

        // 添加内阴影
        g.setColor(new Color(0, 0, 0, 60));
        g.setStroke(new BasicStroke(1));
        g.drawRoundRect(x + 1, y + 1, size - 2, size - 2, 6, 6);

        g.dispose();
    }

    /**
     * 在背景图上创建增强版缺口
     */
    private void cutEnhancedSliderHole(BufferedImage background, int x, int y, int size) {
        cutSimpleSliderHole(background, x, y, size);
    }

    /**
     * 在背景图上挖洞（保持兼容性）
     */
    private void cutSliderHole(BufferedImage background, int x, int y, int size) {
        cutEnhancedSliderHole(background, x, y, size);
    }
    
    /**
     * 图片转Base64
     */
    private String imageToBase64(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        byte[] bytes = baos.toByteArray();
        return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
    }
}


